package cn.itcast.web.shiro;

import cn.itcast.domain.system.Module;
import cn.itcast.domain.system.User;
import cn.itcast.service.system.ModuleService;
import cn.itcast.service.system.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 自定义realm域，需要继承AuthorizingRealm父类
 *  实现抽象方法
 */
public class AuthRealm extends AuthorizingRealm {

	@Autowired
	private UserService userService;

	@Autowired
	private ModuleService moduleService;

	/**
	 *  授权方法
	 *      参数：PrincipalCollection（安全数据集合）
	 *      返回值：AuthorizationInfo（权限数据）
	 *      本质：查询用户可操作的所有权限的名称数组
	 */
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//1.获取安全数据User
		User user = (User)principals.getPrimaryPrincipal();//获取唯一的安全数据
		//2.查询用户可操作的所有模块
		List<Module> moduleList = moduleService.findByUserId(user.getId());
		//3.获取所有模块的名称集合
		Set<String> sets = new HashSet<>();
		for (Module module : moduleList) {
			sets.add(module.getName());
		}
		//4.构造返回值
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.setStringPermissions(sets);
		return info;
	}

	/**
	 * 认证方法
	 *      参数：AuthenticationToken-用户登录输入的用户名和密码
	 *      返回值:
	 *           AuthenticationInfo-认证数据（认证信息）
	 *              安全数据（用户对象）
	 *      本质：查询用户对象，构造安全数据
	 *
	 */
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		//1.获取用户输入的用户名和密码
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		String email = upToken.getUsername();
		String password = new String(upToken.getPassword()); //通过string的构造方法，将char数组转化为字符串
		//2.根据邮箱查询用户
		User user = userService.findByEmail(email);
		//3.判断用户是否存在
		if(user != null) {
			//3.1 如果用户存在，构造AuthenticationInfo返回值
			/**
			 * 通过SimpleAuthenticationInfo的构造方法存入安全数据
			 *      1.安全数据：principal（用户对象）
			 *      2.密码：为了操作便捷，存入数据库密码
			 *      3.realm域名称：约定俗称(当前类名)
			 */
			return new SimpleAuthenticationInfo(user,user.getPassword(),getName());
		}else {
			//3.2 如果用户不存在，返回null
			return null;
		}
	}
}
